// 1. 包声明：指定当前类所属包路径，遵循"项目域+模块"规范，web包专门存放SSM框架的控制器（Controller）
package cn.edu.buu.hospital.web;

// 2. 导入依赖类：引入当前控制器所需的实体、服务、工具类等
// 导入医院实体类：封装医院的属性（如医院ID、名称、地址、电话等），用于前后端数据传输

import cn.edu.buu.hospital.entity.Hospital;
import cn.edu.buu.hospital.service.HospitalService;
import com.github.pagehelper.PageInfo;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * 医院管理控制类
 * 负责处理医院相关的HTTP请求，包括新增医院、查询医院、更新医院、删除医院等功能
 */
// 3. 类注解与声明：标记为Spring MVC控制器，统一映射医院相关URL
@Controller
// 类级请求映射：当前类所有方法的URL都以"/hospital"开头（如"/hospital/add""/hospital/find"）
@RequestMapping("/hospital")
public class HospitalController {

    // 4. 依赖注入：通过@Autowired自动装配Service层Bean，无需手动创建对象
    // 注入医院服务：核心业务逻辑入口，处理医院的增删改查、业务校验等
    @Autowired
    private HospitalService hospitalService; // 注入医院服务，处理医院相关的业务逻辑

    /**
     * 跳转到新增医院页面
     * @return 新增医院页面的视图路径（由视图解析器定位到具体页面）
     */
    // 5. 方法：跳转到新增医院页面（处理GET请求）
    // GET请求映射：URL为"/hospital/toAdd"，前端点击"新增医院"按钮时触发
    @GetMapping("/toAdd")
    public String toAdd() {
        // 返回新增医院页面的视图路径：视图解析器会定位到"backend/hospital/add.html"（如Thymeleaf模板）
        return "backend/hospital/add"; // 返回新增医院页面
    }

    /**
     * 处理新增医院表单提交
     * @param hospitalname 医院名称：从前端表单获取，通过@RequestParam绑定参数
     * @param address 医院地址：从前端表单获取，通过@RequestParam绑定参数
     * @param hphone 医院电话：从前端表单获取，通过@RequestParam绑定参数
     * @param model 模型对象：理论上可传数据，但因重定向会丢失，实际用Session存临时数据
     * @return 成功→重定向到医院列表页；失败→重定向到新增页（带错误信息）
     */
    // 6. 方法：新增医院（处理POST请求）
    // POST请求映射：URL为"/hospital/add"，新增页表单提交时触发
    @PostMapping("/add")
    // 事务注解：声明该方法需事务支持，若新增过程抛异常（如数据库错误），数据库操作会回滚（避免脏数据）
    @Transactional
    public String add(@RequestParam("hospitalname") String hospitalname,
                      @RequestParam("address") String address,
                      @RequestParam("hphone") String hphone,
                      Model model) {
        try {
            // 1. 创建医院实体对象：封装前端传递的医院信息
            Hospital hospital = new Hospital();
            hospital.setHospitalname(hospitalname); // 设置医院名称
            hospital.setAddress(address); // 设置医院地址
            hospital.setHphone(hphone); // 设置医院电话

            // 2. 调用服务层保存医院：业务逻辑由Service层处理（如"医院名称重复校验""数据库插入"）
            hospitalService.add(hospital);
            // 3. 新增成功：重定向到医院列表页（用"redirect:"避免表单重复提交，刷新页面不会重新提交）
            return "redirect:find"; // 新增成功后重定向到医院列表页面
        } catch (Exception e) {
            // 新增失败：捕获异常（业务异常如"医院名称重复"、系统异常如"数据库连接失败"）
            // 4. 获取当前HTTP请求对象：通过RequestContextHolder工具类，非方法参数注入场景下获取request
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            // 5. 保存错误信息到Session：重定向会丢失Model数据，需用Session跨请求传递错误提示
            request.getSession().setAttribute("error", "新增失败: " + e.getMessage());

            // 6. 保存用户输入的医院数据到Session：用于新增页回显（避免用户重新输入已填内容）
            Hospital temp = new Hospital();
            temp.setHospitalname(hospitalname);
            temp.setAddress(address);
            temp.setHphone(hphone);
            request.getSession().setAttribute("addHospital", temp);

            // 7. 重定向到新增页：在新增页显示错误信息和回显数据
            return "redirect:toAdd"; // 新增失败后重定向到新增页面
        }
    }

    /**
     * 分页查询医院列表（支持直接访问列表、条件查询）
     * @param model 模型对象：向前端列表页传递分页数据、查询条件等
     * @param pageNum 当前页码：从URL获取，默认值1（首次访问显示第1页）
     * @param condition 查询条件对象：Spring MVC自动绑定表单查询参数（如医院名称、地址）
     * @return 医院列表页面的视图路径
     */
    // 7. 方法：分页查询医院列表（支持GET/POST请求）
    // 请求映射：URL为"/hospital/find"，支持GET（直接访问列表）和POST（提交查询条件）
    @RequestMapping(value = "/find", method = {RequestMethod.GET, RequestMethod.POST})
    public String find(Model model,
                       // 绑定URL参数"pageNum"，默认值1（用户未传页码时显示第1页）
                       @RequestParam(value = "pageNum", defaultValue = "1") int pageNum,
                       // 绑定查询条件：前端表单提交的"医院名称""地址"等参数自动封装到该对象
                       Hospital condition) {
        // 设置每页显示条数：固定为5条（可根据需求改为参数传递，如@RequestParam接收pageSize）
        int pageSize = 5;
        // 1. 调用服务层分页查询：传入页码、每页条数、查询条件，返回分页结果PageInfo（含当前页数据、总条数等）
        PageInfo<Hospital> pageInfo = hospitalService.find(pageNum, pageSize, condition);

        // 2. 向视图传递数据：供列表页渲染
        model.addAttribute("pageInfo", pageInfo); // 分页结果（核心数据，含当前页医院列表）
        model.addAttribute("condition", condition); // 查询条件（回显到列表页查询表单，方便用户再次查询）
        // 3. 返回医院列表页面视图路径：视图解析器定位到"backend/hospital/list.html"
        return "backend/hospital/list"; // 返回医院列表页面
    }

    /**
     * 跳转到编辑医院页面（根据医院ID查询详情并回显）
     * @param hospitalId 医院ID：从URL获取，前端点击"编辑"按钮时传递
     * @param model 模型对象：向前端编辑页传递医院详情数据
     * @return 医院编辑页面的视图路径
     */
    // 8. 方法：跳转到编辑医院页面（处理GET请求）
    // GET请求映射：URL为"/hospital/toEdit?hospitalId=xxx"，前端编辑按钮触发
    @GetMapping("/toEdit")
    public String toEdit(@RequestParam("hospitalId") Long hospitalId, Model model) {
        // 1. 调用服务层根据ID查询医院详情：获取要编辑的医院数据
        Hospital hospital = hospitalService.findById(hospitalId);
        // 2. 处理查询结果为null的情况：避免前端因空指针报错（如ID不存在时，新建对象并设置ID）
        if (hospital == null) {
            hospital = new Hospital();
            hospital.setHospitalid(hospitalId); // 保留传入的ID，便于前端识别"编辑的是哪个医院"
        }
        // 3. 向视图传递医院数据：编辑页表单回显（如医院名称、地址、电话）
        model.addAttribute("hospital", hospital);
        // 4. 返回编辑页面视图路径：定位到"backend/hospital/edit.html"
        return "backend/hospital/edit"; // 返回编辑页面
    }

    /**
     * 根据医院ID查询详情（用于编辑，逻辑与toEdit一致，可合并避免重复）
     * @param hospitalId 医院ID：从URL获取，前端点击"编辑"按钮时传递
     * @param model 模型对象：向前端编辑页传递医院详情数据
     * @return 医院编辑页面的视图路径
     */
    // 9. 方法：根据ID查询医院（用于编辑，处理GET请求，与toEdit功能重复，可优化合并）
    // GET请求映射：URL为"/hospital/findById?hospitalId=xxx"，前端编辑按钮触发
    @GetMapping("/findById")
    public String findById(@RequestParam("hospitalId") Long hospitalId, Model model) {
        // 1. 调用服务层根据ID查询医院详情
        Hospital hospital = hospitalService.findById(hospitalId);
        // 2. 处理查询结果为null的情况：避免前端空指针
        if (hospital == null) {
            hospital = new Hospital();
            hospital.setHospitalid(hospitalId);
        }
        // 3. 向视图传递医院数据：编辑页表单回显
        model.addAttribute("hospital", hospital);
        // 4. 返回编辑页面视图路径
        return "backend/hospital/edit"; // 返回编辑页面
    }

    /**
     * 处理医院信息修改提交
     * @param hospitalid 医院ID：从表单获取，标识要修改的医院
     * @param hospitalname 新的医院名称：从表单获取
     * @param address 新的医院地址：从表单获取
     * @param hphone 新的医院电话：从表单获取
     * @param model 模型对象：理论上可传数据，实际用Session存临时数据（因重定向丢失Model）
     * @return 成功→重定向到列表页；失败→重定向到编辑页（带错误信息和回显数据）
     */
    // 10. 方法：修改医院信息（处理POST请求）
    // POST请求映射：URL为"/hospital/modify"，编辑页表单提交时触发
    @PostMapping("/modify")
    // 事务注解：声明该方法需事务支持，若修改失败（如数据库错误），回滚操作（避免数据部分更新）
    @Transactional
    public String modify(@RequestParam("hospitalid") Long hospitalid,
                         @RequestParam("hospitalname") String hospitalname,
                         @RequestParam("address") String address,
                         @RequestParam("hphone") String hphone,
                         Model model) {
        try {
            // 1. 创建医院实体对象：封装修改后的医院信息（含ID，标识要更新的记录）
            Hospital hospital = new Hospital();
            hospital.setHospitalid(hospitalid); // 必须设置ID，否则无法定位要修改的医院
            hospital.setHospitalname(hospitalname); // 新的医院名称
            hospital.setAddress(address); // 新的医院地址
            hospital.setHphone(hphone); // 新的医院电话

            // 2. 调用服务层更新医院：业务逻辑由Service层处理（如"医院名称重复校验""数据库更新"）
            hospitalService.modify(hospital);
            // 3. 修改成功：重定向到列表页（避免表单重复提交）
            return "redirect:find"; // 修改成功后重定向到医院列表页面
        } catch (Exception e) {
            // 修改失败：捕获异常（业务异常如"医院名称重复"、系统异常如"数据库连接失败"）
            // 4. 获取当前HTTP请求对象：用于操作Session
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            // 5. 保存错误信息到Session：供编辑页显示
            request.getSession().setAttribute("error", "修改失败: " + e.getMessage());

            // 6. 保存用户输入的修改数据到Session：用于编辑页回显（避免用户重新输入）
            Hospital temp = new Hospital();
            temp.setHospitalid(hospitalid);
            temp.setHospitalname(hospitalname);
            temp.setAddress(address);
            temp.setHphone(hphone);
            request.getSession().setAttribute("editHospital", temp);

            // 7. 重定向到编辑页：携带hospitalId参数，定位到当前编辑的医院，显示错误和回显数据
            return "redirect:toEdit?hospitalId=" + hospitalid; // 修改失败后重定向到编辑页面
        }
    }

    /**
     * 根据医院ID删除医院
     * @param hospitalId 医院ID：从URL获取，前端点击"删除"按钮时传递
     * @return 无论成功失败，均重定向到医院列表页（通过Session显示结果信息）
     */
    // 11. 方法：删除医院（处理GET请求）
    // GET请求映射：URL为"/hospital/removeById?hospitalId=xxx"，前端删除按钮触发
    @GetMapping("/removeById")
    // 事务注解：声明该方法需事务支持，若删除失败（如关联数据存在），回滚操作
    @Transactional
    public String removeById(@RequestParam("hospitalId") Long hospitalId) {
        try {
            // 1. 调用服务层删除医院：Service层会处理关联数据校验（如"医院下是否有科室，有则抛异常"）
            hospitalService.removeById(hospitalId);
            // 2. 删除成功：重定向到列表页
            return "redirect:find"; // 删除成功后重定向到医院列表页面
        } catch (Exception e) {
            // 删除失败：捕获异常（业务异常如"医院有关联科室，无法删除"、系统异常如"数据库错误"）
            // 3. 获取当前HTTP请求对象：用于操作Session
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            // 4. 保存错误信息到Session：供列表页显示
            request.getSession().setAttribute("error", "删除失败: " + e.getMessage());
            // 5. 重定向到列表页：显示错误信息
            return "redirect:find"; // 删除失败后重定向到医院列表页面
        }
    }
}